    /**********************************************************
    ***             Processing.c: EXI decoder               ***
    **********************************************************/


#include "Processing.h"


//++++++++++++++++++++++++++++++++++++++++++++++ misc. global variables ++++++++++++++++++++++++++++++++++++++++++++++++++++


int incnt = 0;                  		// byte counter for incoming EXI stream
unsigned char inbitcnt = 0;     		// bit counter for incoming EXI stream (0-7 in current byte)

unsigned char *Reqbuffer;       		// buffer for incoming EXI request

unsigned int exi_version = 0;			// EXI format version

unsigned char Bitalign = false;			// alignment info 	(default: byte aligned)
unsigned char strict_mode = false;		// strict mode info (default: non-strict)


void setAlignment(unsigned char alig)		// set bit / byte alignment
{
    Bitalign = alig;
}

unsigned char getAlignment()				// get alignment info
{
	return Bitalign;
}

void set_strict_mode(unsigned char mode)	// set strict mode on / off
{
	strict_mode = mode;
}

unsigned char get_strict_mode()				// get strict mode info
{
	return strict_mode;
}


//+++++++++++++++++++++++++++++++++++++++++++++++++ string tables ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// string literals are stored only in debug mode

#ifdef DEBUGMODE

const char* LOCALNAMES_XML[] = {"base", "id", "lang", "space"};

const char* LOCALNAMES_XSI[] = {"nil", "type"};

const char* LOCALNAMES_XS[] = { "ENTITIES", "ENTITY", "ID", "IDREF", "IDREFS",
								"NCName", "NMTOKEN", "NMTOKENS", "NOTATION",
								"Name", "QName", "anySimpleType", "anyType",
								"anyURI", "base64Binary", "boolean", "byte",
								"date", "dateTime", "decimal", "double", "duration",
								"float", "gDay", "gMonth", "gMonthDay", "gYear",
								"gYearMonth", "hexBinary", "int", "integer",
								"language", "long", "negativeInteger",
								"nonNegativeInteger", "nonPositiveInteger",
								"normalizedString", "positiveInteger", "short",
								"string", "time", "token", "unsignedByte",
								"unsignedInt", "unsignedLong", "unsignedShort" };

void create_URI_Table(URI_Table** uTable)
{
	*uTable = (URI_Table*)malloc(sizeof(URI_Table));
	(*uTable)->entries = (struct URI_Entry*)malloc(sizeof(struct URI_Entry));
	(*uTable)->entryCount = 0;
}

void create_Prefix_Table(Prefix_Table** pTable)
{
	(*pTable) = (Prefix_Table*)malloc(sizeof(Prefix_Table));
	(*pTable)->entries = (struct Prefix_Entry*)malloc(sizeof(struct Prefix_Entry));
	(*pTable)->entryCount = 0;
}

void create_LocalNames_Table(LocalNames_Table** lTable)
{
	*lTable = (LocalNames_Table*)malloc(sizeof(LocalNames_Table));
	(*lTable)->entries = (struct LocalNames_Entry*)malloc(sizeof(struct LocalNames_Entry));
	(*lTable)->entryCount = 0;
}


void Init()		// initialize string tables with default entries
{
  	unsigned char i = 0;
  	unsigned char uri_id = 0;
  	unsigned char ln_id = 0;
  	String tmp_uri, tmp_prefix, tmp_lname;

  	create_URI_Table(&uriTable);

  	// URI	0	""

  	String empty;
  	empty.length = 0;
  	empty.str = NULL;

  	uri_id = URI_Table_insertEntry(uriTable, empty);

  	Prefix_Table_insertEntry(uriTable->entries[uri_id].pTable, empty);

  	// URI	1	"http://www.w3.org/XML/1998/namespace"

  	tmp_uri.length = str_length(URI_XML);
  	tmp_uri.str = (unsigned char*)URI_XML;

  	uri_id = URI_Table_insertEntry(uriTable, tmp_uri);

  	tmp_prefix.length = str_length(PREFIX_XML);
  	tmp_prefix.str = (unsigned char*)PREFIX_XML;

  	Prefix_Table_insertEntry(uriTable->entries[uri_id].pTable, tmp_prefix);

  	for(i = 0; i < LOCALNAMES_XML_SIZE; i++)
  	{
  		tmp_lname.length = str_length(LOCALNAMES_XML[i]);
  		tmp_lname.str = (unsigned char*)LOCALNAMES_XML[i];
  		ln_id = LocalNames_Table_insertEntry(uriTable->entries[uri_id].lnTable, tmp_lname);
  	}

  	// URI	2	"http://www.w3.org/2001/XMLSchema-instance"

  	tmp_uri.length = str_length(URI_XSI);
  	tmp_uri.str = (unsigned char*)URI_XSI;

  	uri_id = URI_Table_insertEntry(uriTable, tmp_uri);

  	tmp_prefix.length = str_length(PREFIX_XSI);
  	tmp_prefix.str = (unsigned char*)PREFIX_XSI;

  	Prefix_Table_insertEntry(uriTable->entries[uri_id].pTable, tmp_prefix);

  	for(i = 0; i < LOCALNAMES_XSI_SIZE; i++)
  	{
  		tmp_lname.length = str_length(LOCALNAMES_XSI[i]);
  		tmp_lname.str = (unsigned char*)LOCALNAMES_XSI[i];
  		ln_id = LocalNames_Table_insertEntry(uriTable->entries[uri_id].lnTable, tmp_lname);
  	}

  	// URI	3	"http://www.w3.org/2001/XMLSchema"

  	tmp_uri.length = str_length(URI_XS);
  	tmp_uri.str = (unsigned char*)URI_XS;

  	uri_id = URI_Table_insertEntry(uriTable, tmp_uri);

	for(i = 0; i < LOCALNAMES_XS_SIZE; i++)
	{
  		tmp_lname.length = str_length(LOCALNAMES_XS[i]);
  		tmp_lname.str = (unsigned char*)LOCALNAMES_XS[i];
  		ln_id = LocalNames_Table_insertEntry(uriTable->entries[uri_id].lnTable, tmp_lname);
	}

//	URI_Table_show();
}


unsigned char URI_Table_insertEntry(URI_Table* uTable, String uri)
{
	// allocate memory for next table entry
	struct URI_Entry* temp = uTable->entries;
	uTable->entries = (struct URI_Entry*)malloc((sizeof(struct URI_Entry) * (uTable->entryCount + 1)));
	unsigned char cnt;
	for(cnt = 0; cnt < uTable->entryCount; cnt++) {
		uTable->entries[cnt] = temp[cnt];
	}
	free(temp);

	uTable->entries[uTable->entryCount].value.length = uri.length;
	uTable->entries[uTable->entryCount].value.str = uri.str;

	create_LocalNames_Table(&(uTable->entries[uTable->entryCount].lnTable));
	create_Prefix_Table(&(uTable->entries[uTable->entryCount].pTable));

	unsigned char entryID = uTable->entryCount;
	uTable->entryCount += 1;

	return entryID;
}

void Prefix_Table_insertEntry(Prefix_Table* pTable, String prefix)
{
	// allocate memory for next table entry
	struct Prefix_Entry* temp = pTable->entries;
	pTable->entries = (struct Prefix_Entry*)malloc((sizeof(struct Prefix_Entry) * (pTable->entryCount + 1)));
	unsigned char cnt;
	for(cnt = 0; cnt < pTable->entryCount; cnt++) {
		pTable->entries[cnt] = temp[cnt];
	}
	free(temp);

	pTable->entries[pTable->entryCount].value.length = prefix.length;
	pTable->entries[pTable->entryCount].value.str = prefix.str;

	pTable->entryCount += 1;
}

unsigned char LocalNames_Table_insertEntry(LocalNames_Table* lTable, String localname)
{
	// allocate memory for next table entry
	struct LocalNames_Entry* temp = lTable->entries;
	lTable->entries = (struct LocalNames_Entry*)malloc((sizeof(struct LocalNames_Entry) * (lTable->entryCount + 1)));
	unsigned char cnt;
	for(cnt = 0; cnt < lTable->entryCount; cnt++) {
		lTable->entries[cnt] = temp[cnt];
	}
	free(temp);

	lTable->entries[lTable->entryCount].value.length = localname.length;
	lTable->entries[lTable->entryCount].value.str = localname.str;

	unsigned char entryID = lTable->entryCount;
	lTable->entryCount += 1;

	return entryID;
}


void URI_Table_show()	// print string tables to console for testing
{
	if(uriTable != NULL && uriTable->entryCount > 0)
	{
		printf("\nCurrent URI Table Entries:\n");
		printf("--------------------------\n\n");
		unsigned char i, j;
		for (i = 0; i < uriTable->entryCount; i++)
		{
			printf("Index: %u\n", i);
			printf("URI: %s\n\n", uriTable->entries[i].value.str);

			if (uriTable->entries[i].lnTable != NULL && uriTable->entries[i].lnTable->entryCount > 0)
			{
				printf("LocalName Entries for URI:\n");
				printf("--------------------------\n\n");
				for (j = 0; j < uriTable->entries[i].lnTable->entryCount; j++)
				{
					printf("Index: %u\n", j);
					printf("LocalName: %s\n\n", uriTable->entries[i].lnTable->entries[j].value.str);
				}
				printf("--------------------------\n\n");
			} else {
				printf("No LocalName Entries for URI!\n\n");
			}

			if (uriTable->entries[i].pTable != NULL && uriTable->entries[i].pTable->entryCount > 0)
			{
				printf("Prefix Entries for URI:\n");
				printf("--------------------------\n\n");
				for (j = 0; j < uriTable->entries[i].pTable->entryCount; j++)
				{
					printf("Index: %u\n", j);
					printf("Prefix: %s\n\n", uriTable->entries[i].pTable->entries[j].value.str);
				}
				printf("--------------------------\n\n");
			} else {
				printf("No Prefix Entries for URI!\n\n");
			}
		}
		printf("--------------------------\n\n");
	} else {
		printf("\nURI Table is empty!\n");
	}
}

#endif


//+++++++++++++++++++++++++++++++++++++++++ decode EXI header ++++++++++++++++++++++++++++++++++++++++++++


void Headerproc()
{
    unsigned char tmp;

    if (Reqbuffer[0] == 36)       // EXI Cookie detection
    {
        if (Reqbuffer[1] == 69)
        {
            if (Reqbuffer[2] == 88)
            {
                if (Reqbuffer[3] == 73)
                {
                    incnt = 4;
					#ifdef DEBUGMODE
                    printf("EXI Cookie in Header found and used!\n\n");
					#endif
                }
            }
        }
    }
    tmp = Reqbuffer[incnt]>>6;    	// Distinguishing Bits detection, required
    if (tmp != 2)
    {
		#ifdef DEBUGMODE
        printf("Header Error: Distinguishing Bits not detected, Processing aborted...\n\n");
		#endif
    	return;
    }
    tmp = (Reqbuffer[incnt]>>5)^4;    // EXI Options Bit not supported
    if (tmp != 0)
    {
		#ifdef DEBUGMODE
        printf("Header Error: Options Bit set, but not supported yet!\n\n");
		#endif
    	return;
    }

    // detect EXI version

    tmp = (Reqbuffer[incnt]<<3);
    if (tmp == 128)
    {
		#ifdef DEBUGMODE
        printf("EXI Final Preview Version 1.0 detected!\n\n");
		#endif
    }
    else
    {
        if (tmp > 128)
        {
			#ifdef DEBUGMODE
        	printf("Unknown EXI Final Version!\n\n");
			#endif
        }
        tmp = (Reqbuffer[incnt]<<4);
        tmp = tmp>>4;
        if (tmp<15)
        {
            exi_version = tmp + 1;
        }
        else
        {
            incnt++;
            exi_version += 15;
            while (tmp == 15)
            {
                if (inbitcnt == 0)
                {
                    tmp = (Reqbuffer[incnt]>>4);
                    inbitcnt = inbitcnt + 4;
                } else {
                    tmp = (Reqbuffer[incnt]<<4);
                    tmp = tmp>>4;
                    inbitcnt = 0;
                    incnt++;
                }
                exi_version = exi_version + tmp;
            }
        }
		#ifdef DEBUGMODE
        printf("EXI Final Version %d detected!\n\n", exi_version);
		#endif
    }

    incnt++;                // skip padding bits
    inbitcnt = 0;
}


//++++++++++++++++++++++++++++++++++++++++ misc. help functions ++++++++++++++++++++++++++++++++++++++++++


#ifdef DEBUGMODE
unsigned char str_length(const char *strptr) 	// determine length of given string
{
	const char *s = strptr;
	unsigned char cnt = 0;

	while(*(s++))
	{
		cnt++;
	}
	return cnt;
}
#endif


unsigned char smallLengths[17] = {0, 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4};

unsigned char ld(unsigned char value)	// logarithm to the basis 2 (taken from EXIficient)
{
	if (value < 17) {
		return smallLengths[value];
	} else if (value < 33) {
		// 17 .. 32
		return (unsigned char) 5;
	} else if (value < 65) {
		// 33 .. 64
		return (unsigned char) 6;
	} else if (value < 129) {
		// 65 .. 128
		return (unsigned char) 7;
	}
}


void getbit(unsigned char *Byte, unsigned char n) // read next n bits from EXI stream (max 8 bits)
{
    if (Bitalign == false)
    {
        *Byte = Reqbuffer[incnt];     // if byte aligned, get current byte
        if (n > 0) incnt++;			  // increment byte counter
    } else {
        if ((inbitcnt + n) <= 7)      // if (inbitcnt + n) stays within current byte...
        {
            *Byte = Reqbuffer[incnt]; // get current byte
            *Byte = *Byte<<inbitcnt;  // discard unneeded bits
            *Byte = *Byte>>(8-n);     // shift remaining bits back to LSB
            inbitcnt += n;			  // update bit counter
        } else {                      // if sum reaches into next byte...
            unsigned char temp;
            // store unread bits of current byte in "temp"
            temp = Reqbuffer[incnt]<<inbitcnt;
            temp = temp>>(8-n);		  // shift to LSB position
            n = n - (8-inbitcnt);	  // update n
            inbitcnt = 0;			  // reset bit counter
            incnt++;				  // increment byte counter
            // fill rest of byte with bits from next byte in buffer
            *Byte = Reqbuffer[incnt]>>(8-n);
            *Byte = *Byte^temp;		  // attach bits from "temp"
            inbitcnt = n;			  // update bit counter
        }
    }
}



//++++++++++++++++++++++++++++++++++++++++++ data type decoding ++++++++++++++++++++++++++++++++++++++++++++++++++


unsigned int getUint()			// get Unsigned Integer values from EXI stream
{
    unsigned int value = 0;		// return value
    int mult = 1;				// multiplier
    unsigned char temp = 0;		// working variable
    unsigned char msb;			// msb of current octet
    do {
        getbit(&temp,8);		// read next octet
        msb = temp>>7;			// store octet msb
        temp = temp<<1;			// only want information from least significant 7 bits
        temp = temp>>1;			// replace octet msb with 0
        value += (temp*mult);	// update value
        mult *= 128;			// update multiplier
    } while (msb == 1);			// decoding quit by octet with 0 as msb
    return value;
}

unsigned int getnBitUint(unsigned char n) // get n-Bit Unsigned Integer values from EXI stream
{
	unsigned int value = 0;		// return value

	if (n > 0)
	{
		unsigned char temp = 0;
		if (Bitalign == true)
		{
			getbit(&temp, n); 	// bit aligned -> get n bits
		} else {
			// byte aligned -> get smallest byte number necessary to store n bits
			unsigned char byteanz;
			unsigned char temp1, temp2;
			// determine number of bytes to be read
			if (n%8 == 0) {
				byteanz = n/8;
			} else {
				byteanz = (n/8)+1;
			}
			// least significant byte comes first
			getbit(&temp1, byteanz-1);
			getbit(&temp2, 1);
			temp = temp2<<8;
			temp = temp^temp1;
		}
		value = temp;
	}
	return value;
}

unsigned int getBOOLEAN()	// get boolean values from EXI stream
{
	unsigned int x = getnBitUint(1);

	#ifdef DEBUGMODE
	if (x == 0) {
		printf("Boolean: false\n");
	}
	if (x == 1) {
		printf("Boolean: true\n");
	}
	#endif

	return x;
}

int getINTEGER()	// get signed Integer values from EXI stream
{
	unsigned int value = 0; // return value

	unsigned int sign = getnBitUint(1);	 // 1 bit Unsigned Integer for sign info

	if (sign == 0){ // positive value
		value = getUint();
	}
	if (sign == 1){ // negative value
		value = (getUint()+1)*(-1);
	}

	#ifdef DEBUGMODE
	printf("Integer: %d\n", value);
	#endif

	return value;
}

date getDATETIME()          	// get Date-Time values from EXI stream
{
    date temp;					// return value
    unsigned char temp1, temp2;
    int monthday = 0;
    temp.year = 2000;         	// reference year 2000

    // get year
    getbit(&temp1, 1);          // get sign
    if (temp1 == 1)             // negative
    {
        temp.year -= getUint() + 1;
    } else {
        temp.year += getUint(); // positive
    }

    // get month, day
    getbit(&temp1, 8);          // 9-bit unsigned
    getbit(&temp2, 1);
    if (Bitalign == false)      // byte alignment -> LSB first
    {
        monthday = temp2<<8;
        monthday = monthday^temp1;
    } else {                    // bit aligned -> monthday = 8+1 bits
        monthday = temp1<<1;
        monthday = monthday^temp2;
    }
    temp.day = monthday%32;       // get day
    temp.month = monthday/32;     // get month
    getbit(&temp1, 1);

	#ifdef DEBUGMODE
    printf("Year: %d\n", temp.year);
    printf("Month: %d\n", temp.month);
    printf("Day: %d\n", temp.day);
	#endif

    return temp;
}


unsigned char* getSTRINGValue()       // get String values from AT and CH Events
{
	unsigned char str_len;

	unsigned char *value;						// pointer to return value

	unsigned char uriID = curr_uriID;			// URI Table ID
	unsigned char lnID = curr_lnID;				// LocalNames Table ID

	unsigned char flag_vTable = 0;				// Value Table flag

	unsigned char gvID = 0;						// global value ID
	unsigned char gvBits;						// number of bits to read for gvID

	flag_vTable = (unsigned char)getUint(); // flag -> local / global / new value (if new -> flag = string length + 2 (bytes))

    switch(flag_vTable)
    {
        case 0:     // local value hit -> read lvID and get string from global buffer

        			value = str_buffer;

        			str_len = get_str_value_local(uriID, lnID, value);

                    break;

        case 1:     // global value hit -> read gvID and get string value from global buffer

        			gvBits = ld(str_values_global_num);
					gvID = (unsigned char)getnBitUint(gvBits);

					value = str_buffer;

					str_len = get_str_value_global(gvID, value);

                    break;

        default:    // local and global value miss -> store new value

        			str_len = flag_vTable-2;
					getSTRING_knownLength(str_len); // reads string from EXI stream into str_buffer

					gvID = insert_str_value_global(str_buffer, str_len, uriID, lnID); // store global value

					value = str_buffer;

					str_len = get_str_value_global(gvID, value);

					break;
    }

	#ifdef DEBUGMODE
    printf("\nString Value: '%s'\n", value);
	#endif

    // TODO: return str_len?
    return value;
}


void getSTRING_knownLength(unsigned char str_length)	// read String value with known length into String buffer
{
	unsigned char i = 0;
	unsigned char tmp = 0;

	for(i = 0; i < str_length; i++)
	{
		tmp = (unsigned char)getUint();
		str_buffer[i] = tmp;
	}
}


#ifdef DEBUGMODE
String getSTRING_unknownLength()		// get String value (read length from EXI stream)
{
	unsigned int string_length = 0;
	string_length = getUint();

	unsigned char temp[string_length];

	String string_val;
	string_val.str = temp;
	string_val.length = string_length;

	getSTRING_knownLength((unsigned char)string_length);

	unsigned char i;
	for(i=0; i<(unsigned char)string_length; i++){
		string_val.str[i] = str_buffer[i];
	}

	return string_val;
}
#endif


void getQName()	// decode QName information after generic SE & AT events
{
	unsigned char flag_uriTable = 0;	// URI Table flag
	unsigned char flag_lnTable = 0;		// LocalName Table flag
	unsigned char uriID = 0;			// URI Table ID
	unsigned char lnID = 0;				// LocalName Table ID

	unsigned char uribits = ld(uricount);

	flag_uriTable = (unsigned char)getnBitUint(uribits);

	if(flag_uriTable == 0) // URI miss -> URI String follows in EXI file
	{
		// -> will not occur if everything is Schema-informed!

		#ifdef DEBUGMODE
		// get String and insert URI in URI Table
		String uriStr;
		uriStr = getSTRING_unknownLength();
		uriID = URI_Table_insertEntry(uriTable, uriStr);
		curr_qname.uri = &(uriTable->entries[uriID].value);
		#endif
	}
	else // URI hit -> uriID = flag_uriTable-1
	{
		uriID = flag_uriTable-1;

		#ifdef DEBUGMODE
		curr_qname.uri = &(uriTable->entries[uriID].value);
		#endif
	}

	flag_lnTable = (unsigned char)getUint();

	if(flag_lnTable == 0) // LocalName hit -> lnID comes next
	{
		unsigned char lnbits = ld(localnamecount[uriID]);
		lnID = (unsigned char)getnBitUint(lnbits);

		#ifdef DEBUGMODE
		curr_qname.localName = &(uriTable->entries[uriID].lnTable->entries[lnID].value);
		#endif
	}
	else // LocalName miss -> flag = (length of LocalName String) + 1
	{
		// -> will not occur if everything is Schema-informed!

		#ifdef DEBUGMODE

		// get String and insert into LocalName Table

		String lnStr;
		lnStr.length = flag_lnTable - 1;
		unsigned char temp[flag_lnTable];
		lnStr.str = temp;

		getSTRING_knownLength(flag_lnTable - 1);

		unsigned char i;
		for(i=0; i<flag_lnTable; i++){
			lnStr.str[i] = str_buffer[i];
		}

		if(uriTable->entries[uriID].lnTable == NULL)
		{
			create_LocalNames_Table(&(uriTable->entries[uriID].lnTable));
		}
		lnID = LocalNames_Table_insertEntry(uriTable->entries[uriID].lnTable, lnStr);
		curr_qname.localName = &(uriTable->entries[uriID].lnTable->entries[lnID].value);

		#endif
	}

	curr_uriID = uriID;
	curr_lnID = lnID;
}


/*
 * evaluate data type after generic AT(*) event -> jump to related decoding function
 * also called by FSMs for decoding of all LIST & ENUMERATION data types
 */
void getData()
{
	switch(datatype)
	{
		case STRING:

			string_value = getSTRINGValue();

			//TODO: callback (uriID, lnID, value / strlen)

			break;

		case INTEGER:

			int_value = getINTEGER();

			//TODO: callback (uriID, lnID, value)

			break;

		case DATETIME:

			date_value = getDATETIME();

			//TODO: callback (uriID, lnID, value)

			break;

		case BOOLEAN:

			bool_value = getBOOLEAN();

			//TODO: callback (uriID, lnID, value)

			break;

		case UNSIGNED_LONG:

			ulong_value = getUint();

			#ifdef DEBUGMODE
			printf("\nUnsigned long: %u\n\n", ulong_value);
			#endif

			//TODO: callback (uriID, lnID, value)

			break;

		case LIST_TYPE:
			;
			unsigned char listlength = (unsigned char)getUint();

			#ifdef DEBUGMODE
			printf("\nLIST length: %u\n", listlength);
			#endif

			datatype = list_datatype;

			// decode all LIST elements of datatype "list_datatype"
			unsigned char i;
			for(i = 0; i < listlength; i++)
			{
				getData(); // recursive function call for list datatype
			}

			break;

		case ENUMERATION:
			;
			// just get enumeration position index here...

			enum_position = (unsigned char)getnBitUint(enum_codelength);

			#ifdef DEBUGMODE
			printf("\nEnumeration Position: %u\n", enum_position);
			#endif

			break;

		default:
			break;
	}
}


//+++++++++++++++++++++++++++++++++++++++++ Value Storage ++++++++++++++++++++++++++++++++++++++++


void reset_str_values_global(void) {
	str_values_global_num=0;
}


// insert new global value, returns global ID
unsigned char insert_str_value_global(unsigned char *str, unsigned char strlen, unsigned char uriID, unsigned char lnID)
{
	unsigned char i;
	unsigned char length=0;

	// determine current global buffer length
	for(i=0; i<str_values_global_num; i++)
		length+=str_values_global_lengths[i]+2;

	// insert new string (starts with URI- & LocalName-ID)
	str_values_global[length++]=uriID;
	str_values_global[length++]=lnID;

	for(i=0; i<strlen; i++)
		str_values_global[length+i]=str[i];

	// store strlen
	str_values_global_lengths[str_values_global_num]=strlen;

	// increment element count
	str_values_global_num++;

	// return global ID
	return str_values_global_num-1;
}


// get global value at given index, returns str length
unsigned char get_str_value_global(unsigned char id, unsigned char *str)
{
	unsigned char i;
	unsigned char length=0;

	// error handling
	if(id >= str_values_global_num)
		return 0;

	// get buffer position where string starts
	for(i=0; i<id; i++)
		length+=str_values_global_lengths[i]+2;

	// get string
	for(i=2; i<str_values_global_lengths[id]+2; i++)
		str[i-2]=str_values_global[length+i];

	// add NULL char
	str[str_values_global_lengths[id]]='\0';

	// return strlen
	return str_values_global_lengths[id];
}


// get local value, returns str length
unsigned char get_str_value_local(unsigned char uriID, unsigned char lnID, unsigned char *str)
{
	unsigned char i;
	unsigned char length=0;
	unsigned char globalID=0;

	unsigned char lvNum=0;						// number of local values (global values for specific uriID-lnID-combination)
	unsigned char lvID=0;						// local value ID
	unsigned char lvBits;						// number of bits to read for lvID

	// browse global values, determine number of local values
	for(i=0; i<str_values_global_num; i++)
	{
		// uriID-lnID hit -> increment lvNum
		if(str_values_global[length]==uriID && str_values_global[length+1]==lnID)
			lvNum++;

		length+=str_values_global_lengths[i]+2;
	}

	// read local value ID from EXI stream
	lvBits = ld(lvNum);
	lvID = (unsigned char)getnBitUint(lvBits);

	// error handling
	if (lvID >= lvNum)
		return 0;

	length = 0;

	// browse global values, look for local value matching lvID
	unsigned char lvID_comp = 0;
	for(i=0; i<str_values_global_num; i++)
	{
		globalID = i;

		// uriID-lnID hit
		if(str_values_global[length]==uriID && str_values_global[length+1]==lnID)
		{
			if (lvID_comp == lvID)
			{
				unsigned char tmp[str_values_global_lengths[globalID]+1];
				str = tmp;

				// get string
				for(i=2; i<str_values_global_lengths[globalID]+2; i++)
					str[i-2]=str_values_global[length+i];

				// add NULL char
				str[str_values_global_lengths[globalID]]='\0';

				// found local value, terminate search
				break;
			}
			lvID_comp++;
		}
		length+=str_values_global_lengths[i]+2;
	}

	// return strlen
	return str_values_global_lengths[globalID];
}


//+++++++++++++++++++++++++++++++++++++++++ Main for EXI-Processing ++++++++++++++++++++++++++++++++++++++++

void Processing(unsigned char *inbuffer)
{
    Reqbuffer = inbuffer;

	curr_lnID = 0;
	curr_uriID = 0;

	datatype = NONE;
	list_datatype = NONE;

	enum_codelength = 0;
	enum_position = 0;


	// initialize URI/LName/Value counters & Value Table...

	unsigned char i;

	uricount = 4; // 4 initial default namespaces

	localnamecount[0] = 0;
	localnamecount[1] = 4;
	localnamecount[2] = 46;
	localnamecount[3] = 2;

	for(i=4; i<MAX_URIS; i++)
	{
		localnamecount[i] = 0;
	}


	str_values_global_num = 0;

	for(i=0; i<64; i++) {
		str_values_global_lengths[i] = 0;
	}


	#ifdef DEBUGMODE
    Init();					// initialize String Tables (URI, Prefix, LocalName)
	#endif


    Headerproc();           // EXI Header Processing

    FSM_Start();            // start generated schema-informed FSMs

}

